# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: helpers\intents\__init__.py
import logging, uuid, datetime, random
from dateutil.tz import tzlocal
from sqlalchemy import func as sql_func
import sqlalchemy.exc
from db import Connection
from db.tables.intents import IntentsTable, IntentRow
from db.data.users import get_user_shard, prepare_user_data
from db.tables.users import UserRow, UsersTable, UserConfirmation
from db.tables.events import create_event
from server.http_responses.http_errors import Http409, ApiErrors
from helpers.hashing import md5
from helpers.emails import is_email_banned
from helpers.setup_types import *
from settings_provider import settings
from helpers.account import reset_ui_sessions

logger = logging.getLogger("helpers.intents")


def add_intent(connection, data, expires, unique_key, regenerate_on_collision=False):
    if random.random() < 0.01:
        q = IntentsTable.delete().where(
            sql_func.current_timestamp() > IntentRow.expires
        )
        connection.execute(q)
    intent_id = str(uuid.uuid4())
    if regenerate_on_collision:
        delete_query = IntentsTable.delete().where(IntentRow.unique_key == unique_key)
        connection.execute(delete_query)
    insert_query = IntentsTable.insert().values(
        intent_id=intent_id, unique_key=unique_key, expires=expires, data=data
    )
    try:
        connection.execute(insert_query)
    except sqlalchemy.exc.IntegrityError:
        if unique_key:
            update_query = (
                IntentsTable.update(values=dict(expires=expires, intent_id=intent_id))
            ).where(IntentRow.unique_key == unique_key)
            connection.execute(update_query)

    return intent_id


def create_email_change_intent(user_id, old_email, new_email, expire_days=2):
    data = dict(
        action="change_email", user_id=user_id, old_email=old_email, new_email=new_email
    )
    unique_key = md5("change_email" + user_id + old_email + new_email)
    with Connection("master") as (connection):
        return add_intent(
            connection,
            data,
            sql_func.current_timestamp() + (datetime.timedelta(days=expire_days)),
            unique_key,
        )


def create_password_reset_intent(user_id, expire_days=2):
    data = dict(action="password_reset", user_id=user_id)
    unique_key = md5("password_reset" + user_id)
    with Connection("master") as (connection):
        return add_intent(
            connection,
            data,
            sql_func.current_timestamp() + (datetime.timedelta(days=expire_days)),
            unique_key,
        )


def create_new_account_intent(user_data, expire_days=2):
    data = dict(user_data)
    data["shard_id"] = "master"
    data["action"] = "new_account"
    unique_key = md5("new_account" + str(random.random()))
    with Connection("master") as (connection):
        return add_intent(
            connection,
            data,
            sql_func.current_timestamp() + (datetime.timedelta(days=expire_days)),
            unique_key,
        )


def create_otp_reset_intent(user_id, expire_days=2):
    data = dict(action="otp_reset", user_id=user_id)
    unique_key = md5("otp_reset" + user_id)
    with Connection("master") as (connection):
        return add_intent(
            connection,
            data,
            sql_func.current_timestamp() + (datetime.timedelta(days=expire_days)),
            unique_key,
        )


def consume_intent_change_email(data, extra_data, remote_address=None):
    user_shard = get_user_shard(data["user_id"])
    with Connection(user_shard) as (connection):
        user = prepare_user_data(connection, user_id=data["user_id"])
        if user["deleted_at"]:
            logger.info("user deleted or disabled %s", user["email"])
            return
        if user["email"] != data["old_email"]:
            return
        update = (UsersTable.update(values=dict(email=data["new_email"]))).where(
            UserRow.user_id == data["user_id"]
        )
        try:
            connection.execute(update)
        except sqlalchemy.exc.IntegrityError as e:
            if '"ix_users_unique_active_email"' in str(e):
                raise Http409("Email already exist")
            raise

        if user["owner_id"] is None:
            owner_id = user["user_id"]
        else:
            owner_id = user["owner_id"]
        event_query = create_event(
            "account_email_changed", owner_id, user["user_id"], shard=user_shard
        )
        connection.execute(event_query)
        if has_feature(SetupFeatures.SALES_UPDATES):
            if user["owner_id"] is None:
                from helpers.sales.events import user_email_changed

                user["email"] = data["new_email"]
                user_email_changed(user, remote_address)


def consume_intent_password_reset(data, extra_data, remote_address=None):
    user_shard = get_user_shard(data["user_id"])
    new_password = extra_data.get("new_password")
    if not new_password:
        raise Http409("new_password not found")
    ph = settings["password_hashing_strategy"]
    new_password = ph(new_password, settings.get("password_hash_salt"))
    with Connection(user_shard) as (db):
        user = prepare_user_data(db, user_id=data["user_id"])
        if user["deleted_at"] or not user["enabled"]:
            logger.info("user deleted or disabled %s", user["email"])
            return
        update = dict(password=new_password)
        from helpers.account import require_password_is_not_reused

        if new_password == user["password"]:
            if user["pwd_history"]:
                raise Http409("password reused", code=ApiErrors.PASSWORD_REUSE_ERROR)
        else:
            if user["pwd_max_age"]:
                update["pwd_expires"] = datetime.datetime.now(tzlocal()) + (
                    datetime.timedelta(days=user["pwd_max_age"])
                )
            else:
                update["pwd_expires"] = None
            require_password_is_not_reused(
                db, user["user_id"], user["pwd_history"], new_password, user["password"]
            )
        update = (UsersTable.update(values=update)).where(
            UserRow.user_id == data["user_id"]
        )
        db.execute(update)
        if user["owner_id"] is None:
            owner_id = user["user_id"]
        else:
            owner_id = user["owner_id"]
        event_query = create_event(
            "account_password_reset", owner_id, user["user_id"], shard=user_shard
        )
        db.execute(event_query)
        reset_ui_sessions(db, user["user_id"])


def consume_intent_new_account(data, extra_data, remote_address=None):
    user_shard = data["shard_id"]
    ph = settings["password_hashing_strategy"]
    new_password = ph(data["password"], settings.get("password_hash_salt"))
    email = data["email"].lower()
    if is_email_banned(email):
        raise Http409("invalid email")
    confirmation = UserConfirmation.NEEDS_VALIDATION
    extra = dict(
        campaign_id=data.get("campaign_id"),
        reseller=data.get("reseller"),
        web_consultant=data.get("web_consultant"),
        utm_source=data.get("utm_source"),
        registration_address=data.get("remote_address"),
    )
    with Connection(user_shard) as (connection):
        user = dict(
            user_id=str(uuid.uuid4()),
            owner_id=None,
            email=email,
            password=new_password,
            license_key=None,
            api_key=None,
            enabled=True,
            first_name=data["first_name"],
            last_name=data["last_name"],
            phone=data.get("phone"),
            country=data.get("country"),
            website=data.get("website"),
            company=data.get("company"),
            confirmation=confirmation,
            license_type="trial",
            role="master",
            extra=extra,
            notifications=dict(scans=True, updates=False, monthly_status=True),
        )
        q = UsersTable.insert().values(**user)
        try:
            connection.execute(q)
        except sqlalchemy.exc.IntegrityError as e:
            if '"ix_users_unique_active_email"' in str(e):
                raise Http409("Email already exist")
            raise

        event_data = dict(remote_address=data["remote_address"])
        try:
            event_data["campaign_id"] = data["campaign_id"]
        except KeyError:
            pass

        try:
            event_data["web_consultant"] = data["web_consultant"]
        except KeyError:
            pass

        try:
            event_data["reseller"] = data["reseller"]
        except KeyError:
            pass

        event_query = create_event(
            "account_created",
            user["user_id"],
            user["user_id"],
            shard=user_shard,
            data=event_data,
        )
        connection.execute(event_query)
        if has_feature(SetupFeatures.SALES_UPDATES):
            from helpers.sales.events import new_user

            new_user(user, remote_address)


def consume_otp_reset(data, extra_data, remote_address=None):
    user_shard = get_user_shard(data["user_id"])
    with Connection(user_shard) as (connection):
        user = prepare_user_data(connection, user_id=data["user_id"])
        if user["deleted_at"] or not user["enabled"]:
            logger.info("user deleted or disabled %s", user["email"])
            return
        update = (UsersTable.update(values=dict(otp=None))).where(
            UserRow.user_id == data["user_id"]
        )
        connection.execute(update)
        if user["owner_id"] is None:
            owner_id = user["user_id"]
        else:
            owner_id = user["owner_id"]
        event_query = create_event(
            "account_otp_reset",
            owner_id,
            user["user_id"],
            shard=user_shard,
            data=dict(remote_address=remote_address),
        )
        connection.execute(event_query)


intent_handlers = {
    "password_reset": consume_intent_password_reset,
    "new_account": consume_intent_new_account,
    "change_email": consume_intent_change_email,
    "otp_reset": consume_otp_reset,
}
